﻿using Pay.DTO;
using System.Net;
using System.Text.Encodings.Web;
using System.Text.Json;

namespace Pay.Core
{
	/// <summary>
	/// 全局异常处理
	/// </summary>
	public class ExceptionMiddleware
	{
		private readonly RequestDelegate _next;
		private readonly JsonSerializerOptions _jsonSerializerOptions;

		/// <summary>
		/// ctor
		/// </summary>
		public ExceptionMiddleware(RequestDelegate next)
		{
			_next = next;
			_jsonSerializerOptions = new JsonSerializerOptions
			{
				PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
				Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
			};
		}

		/// <summary>
		/// Invoke
		/// </summary>
		public async Task Invoke(HttpContext context)
		{
			//保存原始流
			var originalStream = context.Response.Body;
			try
			{
				//应答内存流，替换原有流
				var repMS = new MemoryStream();
				//替换原有应答流
				context.Response.Body = repMS;

				await _next(context);

				//应答数据
				var repData = await ToResponseContent(context, repMS, originalStream);
				//token验证失效，返回相关错误信息
				if (context.Response.StatusCode == 401)
				{
					await WriteErrorResponseAsync(context, 401, "token验证失败", originalStream);
				}
			}
			catch (Exception ex)
			{
				await WriteErrorResponseAsync(context, (int)HttpStatusCode.InternalServerError, $"{ex.Message}|{ex.StackTrace}", originalStream);
			}
		}

		#region private methods
		private async Task WriteErrorResponseAsync(HttpContext context, int statusCode, string message, Stream origin)
		{
			//发生错误时，格式化返回信息
			var response = new BaseResultDto
			{
				Code = statusCode,
				Msg = message
			};
			context.Response.Body = origin;
			context.Response.ContentType = "text/json;charset=utf-8;";
			await context.Response.WriteAsync(JsonSerializer.Serialize(response, _jsonSerializerOptions));
		}

		/// <summary>
		/// 获取请求体内容
		/// </summary>
		private async Task<Dictionary<string, string>> ToRequestContent(HttpContext context)
		{
			var result = new Dictionary<string, string>();
			var request = context.Request;
			if (request != null)
			{
				try
				{
					if (request.Headers != null)
					{
						foreach (var item in request.Headers)
						{
							result.TryAdd(item.Key, item.Value);
						}
					}

					if (request.HasJsonContentType())
					{
						request.EnableBuffering();
						request.Body.Position = 0;
						var reader = new StreamReader(request.Body, leaveOpen: true);
						var data = await reader.ReadToEndAsync();
						request.Body.Position = 0;
						result.TryAdd("data", data);
					}

					if (request.HasFormContentType)
					{
						foreach (var item in request.Form)
						{
							result.TryAdd($"form-{item.Key}", item.Value);
						}

						if (request.Form.Files != null)
						{
							for (var i = 0; i < request.Form.Files.Count; i++)
							{
								var formFile = request.Form.Files[i];
								result.TryAdd($"{formFile.FileName}-{i + 1}", $"{formFile.ContentDisposition}_{formFile.Length}");
							}
						}
					}
				}
				catch { }
			}

			return result;
		}

		/// <summary>
		/// 获取回应内容
		/// </summary>
		private async Task<string> ToResponseContent(HttpContext context, MemoryStream ms, Stream original)
		{
			var response = context.Response;
			var result = string.Empty;
			if (response != null)
			{
				var contentType = response.ContentType ?? string.Empty;
				if (contentType.Contains("application/json") || contentType.Contains("application/xml") || contentType.Contains("text/"))
				{
					var reader = new StreamReader(ms);
					ms.Position = 0;
					result = await reader.ReadToEndAsync();
				}

				//恢复原始流
				ms.Position = 0;
				await ms.CopyToAsync(original);
				context.Response.Body = original;
			}

			await ms.DisposeAsync();
			return result;
		}
		#endregion
	}
}
